package com.yoke.assistant.remote;

import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
 *  
 * @author songfeng 
 * @version 1.0 
 * @since 2015-10-24 
 * @category com.feng.test.longconnection 
 * 
 */  
public class Server  
{

    private static final int MAX_SIZE = 256; // max size 256
    public static final int PORT = 8999;
    private static Charset mCharSet = Charset.forName("UTF-8"); //encode and decode charset

    public static void main(String args[]) {
        try {
            Selector selector = Selector.open();    // init an selector
            initServerChannel(selector);
            startSelector(selector);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void initServerChannel(Selector selector) {
        ServerSocketChannel serverChannel = null;
        try {
            serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false); // not blocking
            serverChannel.socket().bind(new InetSocketAddress(PORT));

            serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 将ServerChannl注册为accept感兴趣类型
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void startSelector(Selector selector) {
        int loopCount = 0;
        while (true) {
            int n = 0;    // Selector轮询注册来的Channel, 阻塞到至少有一个通道在你注册的事件上就绪了。
            try {
                n = selector.select();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (n == 0) {
                continue;
            }
            System.out.println("loopCount:" + loopCount);
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();    // 获取SelectionKey
                SocketChannel socketChannel = null;
                SelectableChannel selectableChannel = selectionKey.channel();

                // 每一步都要判断selectionKey.isValid()，避免断开连接产生的java.nio.channels.CancelledKeyException
                if (selectionKey.isValid() && selectionKey.isAcceptable()) {
                    System.out.println("selectionKey isAcceptable");
                    acceptClient(selectionKey, (ServerSocketChannel) selectableChannel);
                }
                if (selectionKey.isValid() && selectionKey.isReadable()) {
                    // a channel is ready for reading
                    System.out.println("selectionKey isReadable");
                    socketChannel = (SocketChannel) selectableChannel;// 返回为之创建此键的通道。
                    readMsg(selectionKey, socketChannel);
                }
                if (selectionKey.isValid() && selectionKey.isWritable()) {
                    // a channel is ready for writing
                    System.out.println("selectionKey isWritable");
                }
                iterator.remove();
            }
            loopCount++;
        }
    }

    private static void acceptClient(SelectionKey selectionKey, ServerSocketChannel serverChannel) {
        // 此方法返回的套接字通道（如果有）将处于阻塞模式。
        try {
            SocketChannel socketChannel = serverChannel.accept();
            socketChannel.configureBlocking(false);

            // 向Selector注册Channel，设置读取为感兴趣操作，此类操作将会在下一次选择器select操作时被交付。同时附加byteBuffer对象作为数据传递的容器
            socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ | SelectionKey.OP_WRITE);

            if (VERSION.SDK_INT >= VERSION_CODES.N) {
                System.out.println("connected from:" + socketChannel.getRemoteAddress());
            }
        } catch (IOException e) {
            e.printStackTrace();
            selectionKey.cancel();
        }
    }

    private static void readMsg(SelectionKey selectionKey, SocketChannel socketChannel) {
        if (selectionKey == null || socketChannel == null) {
            return;
        }
        ByteBuffer dataBuffer = ByteBuffer.allocate(MAX_SIZE);
        int count = 0;
        try {
            count = socketChannel.read(dataBuffer);
        } catch (IOException e) {
            e.printStackTrace();
            selectionKey.cancel();
            try {
                socketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        if (count > 0) {
            dataBuffer.flip();
            byte[] bytes = new byte[dataBuffer.remaining()];
            dataBuffer.get(bytes);
            String data = new String(bytes, mCharSet);
            System.out.println("received: " + data);
            selectionKey.attach(dataBuffer);    // 给SelectionKey附加上的DataBuffer对象
        }
        if (count == -1) {
            selectionKey.cancel();
            try {
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static void writeMsg(SelectionKey selectionKey, SocketChannel socketChannel) {
        if (selectionKey == null || socketChannel == null) {
            return;
        }
        ByteBuffer dataBuffer = (ByteBuffer) selectionKey.attachment();
        //输出到通道
        try {
            while (dataBuffer.hasRemaining()) {
                socketChannel.write(dataBuffer);
            }
            //将缓冲区的当前位置和界限之间的字节（如果有）复制到缓冲区的开始处
            dataBuffer.compact();
        } catch (IOException e) {
            e.printStackTrace();
            selectionKey.cancel();
            try {
                socketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}  